package se.perp.c4.control;

import java.awt.Point;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;
import javax.swing.SwingWorker;
import se.perp.c4.control.iface.IAnimationListener;
import se.perp.c4.model.Actor;
import se.perp.c4.model.Actor.EActor;
import se.perp.c4.model.ActorCollection;
import se.perp.c4.model.Move;
import se.perp.c4.model.Territory.ETerritory;
import se.perp.c4.model.TerritoryCollection;
import se.perp.c4.ui.GamePiece;
import se.perp.c4.ui.PieceCollection;
import se.perp.c4.ui.PieceOrderer;
import se.perp.c4.ui.RegionBounds;

class Animation extends SwingWorker<Void, Void> {

    private final Move move;
    private final boolean performActualMove;
    private final boolean orderPiecesWhenDone;
    private final List<IAnimationListener> listeners = new LinkedList<IAnimationListener>();
    private final PieceCollection pieceColl;
    private final TerritoryCollection terrColl;
    private final ActorCollection actorColl;
    private final EActor who;
    private final ETerritory to;
    private final GamePiece piece;
    private final Point p1,  p0;
    private final double xstep,  ystep;

    public Animation(
            final PieceCollection pc,
            final TerritoryCollection tc,
            final ActorCollection ac,
            final Move move,
            final IAnimationListener l,
            final boolean performActualMove,
            final boolean orderPiecesWhenDone) {
        this.pieceColl = pc;
        this.terrColl = tc;
        this.actorColl = ac;
        this.move = move;
        this.performActualMove = performActualMove;
        if (l != null) {
            listeners.add(l);
        }
        this.orderPiecesWhenDone = orderPiecesWhenDone;

        who = move.attacker;
        to = move.to;

        piece = pieceColl.get(who);

        if (move.defender == EActor.NOBODY) {
            p1 = RegionBounds.getCenterPoint(to);
            p1.x -= piece.getWidth() / 2;
            p1.y -= piece.getHeight() / 2;
        } else {
            p1 = pieceColl.get(move.defender).getLocation();
        }

        p0 = piece.getLocation();

        xstep = (p1.x - p0.x) / (double) Animator.NSTEPS;
        ystep = (p1.y - p0.y) / (double) Animator.NSTEPS;
    }

    @Override
    protected Void doInBackground() throws Exception {

        double x = p0.x, y = p0.y;
        for (int step = 0; step < Animator.NSTEPS; x += xstep, y += ystep, step++) {
            piece.setLocation((int) x, (int) y);

            try {
                Thread.sleep(Animator.DELAY);
            } catch (InterruptedException e) {
            }
        }

        if (performActualMove) {
            Actor attacker = actorColl.get(move.attacker);
            terrColl.moveActor(attacker, move.to);
        }

        if (orderPiecesWhenDone) {
//                Territory fromT = terrColl.get(move.from);
//                Territory toT = terrColl.get(move.to);
//                if (fromT != null) {
//                    PieceOrderer.orderIn(terrColl, pieceColl, fromT);
//                }
//                if (toT != null) {
//                    PieceOrderer.orderIn(terrColl, pieceColl, toT);
//                }
            PieceOrderer.orderAll(terrColl, pieceColl);
        }


        for (IAnimationListener l : listeners) {
            l.animationFinished();
        }

        return null;
    }

    public void addAnimationListener(IAnimationListener l) {
        listeners.add(l);
    }
}

public class Animator implements IAnimationListener {

    public static final int NSTEPS = 33;
    public static final int DELAY = 33;
    public static final int TIME = NSTEPS * DELAY;
    private boolean isPlaying = false;
    private ActorCollection actorColl;
    private TerritoryCollection terrColl;
    private PieceCollection pieceColl;
    private final Queue<Animation> animationQueue = new LinkedList<Animation>();

    public void setup(
            final ActorCollection ac,
            final TerritoryCollection tc,
            final PieceCollection pc) {
        actorColl = ac;
        pieceColl = pc;
        terrColl = tc;
    }

    public synchronized boolean isPlaying() {
        return isPlaying;
    }

    private synchronized void setPlaying(boolean b) {
        isPlaying = b;
    }

    public void queueAnimation(
            final Move move,
            final IAnimationListener l,
            final boolean performActualMove,
            final boolean orderPiecesWhenDone) {
        Animation anim = new Animation(pieceColl, terrColl, actorColl, move, l, performActualMove, orderPiecesWhenDone);
        anim.addAnimationListener(this);
        synchronized (animationQueue) {
            animationQueue.add(anim);
        }
    }

    public void start() {
        if (!isPlaying()) {
            Animation anim;
            synchronized (animationQueue) {
                anim = animationQueue.poll();
            }
            setPlaying(true);
            anim.execute();
        }
    }

    public Animator() {
    }

    public void animationFinished() {
        synchronized (animationQueue) {
            if (animationQueue.isEmpty()) {
                setPlaying(false);
            } else {
                Animation anim = animationQueue.poll();
                anim.execute();
            }
        }
    }
}
